{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Iteration 기능과 사람 개입(Human-in-the-loop)\n",
    "\n",
    "`iter()` 메서드는 에이전트의 실행 과정을 단계별로 반복할 수 있게 해주는 반복자(iterator)를 생성합니다.\n",
    "\n",
    "중간 과정에서 사용자의 입력을 받아 계속 진행할지 묻는 기능을 제공합니다. 이를 `Human-in-the-loop` 라고 합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# API KEY를 환경변수로 관리하기 위한 설정 파일\n",
    "from dotenv import load_dotenv\n",
    "\n",
    "# API KEY 정보로드\n",
    "load_dotenv()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# LangSmith 추적을 설정합니다. https://smith.langchain.com\n",
    "# !pip install -qU langchain-teddynote\n",
    "from langchain_teddynote import logging\n",
    "\n",
    "# 프로젝트 이름을 입력합니다.\n",
    "logging.langsmith(\"CH15-Agents\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "먼저, 도구(tool) 를 정의합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.agents import tool\n",
    "\n",
    "\n",
    "@tool\n",
    "def add_function(a: float, b: float) -> float:\n",
    "    \"\"\"Adds two numbers together.\"\"\"\n",
    "    return a + b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "다음으로는 `add_function` 을 사용하여 덧셈 계산을 수행하는 Agent 를 정의합니다."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder\n",
    "from langchain_openai import ChatOpenAI\n",
    "from langchain.agents import create_tool_calling_agent, AgentExecutor\n",
    "\n",
    "# 도구 정의\n",
    "tools = [add_function]\n",
    "\n",
    "# LLM 생성\n",
    "gpt = ChatOpenAI(model=\"gpt-4o-mini\")\n",
    "\n",
    "# prompt 생성\n",
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"You are a helpful assistant.\",\n",
    "        ),\n",
    "        (\"human\", \"{input}\"),\n",
    "        MessagesPlaceholder(variable_name=\"agent_scratchpad\"),\n",
    "    ]\n",
    ")\n",
    "\n",
    "# Agent 생성\n",
    "gpt_agent = create_tool_calling_agent(gpt, tools, prompt)\n",
    "\n",
    "# AgentExecutor 생성\n",
    "agent_executor = AgentExecutor(\n",
    "    agent=gpt_agent,\n",
    "    tools=tools,\n",
    "    verbose=False,\n",
    "    max_iterations=10,\n",
    "    handle_parsing_errors=True,\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### AgentExecutor의 iter()\n",
    "\n",
    "이 메서드는 AgentExecutor의 실행 과정을 단계별로 반복할 수 있게 해주는 반복자(iterator)를 생성합니다.\n",
    "\n",
    "**함수 설명**\n",
    "`iter()` 는 에이전트가 최종 출력에 도달하기까지 거치는 단계들을 순차적으로 접근할 수 있는 `AgentExecutorIterator` 객체를 반환합니다.\n",
    "\n",
    "**주요 기능**\n",
    "- **단계별 실행 접근**: 에이전트의 실행 과정을 단계별로 살펴볼 수 있습니다."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**흐름 정리**\n",
    "\n",
    "`\"114.5 + 121.2 + 34.2 + 110.1\"` 의 덧셈 계산을 수행하기 위해서는 단계별로 계산이 수행되게 됩니다.\n",
    "\n",
    "1. 114.5 + 121.2 = 235.7\n",
    "2. 235.7 + 34.2 = 270.9\n",
    "3. 270.9 + 110.1 = 381.0\n",
    "\n",
    "이러한 계산 과정을 단계별로 살펴볼 수 있습니다.\n",
    "\n",
    "이때, \n",
    "\n",
    "단계별로 계산 결과를 사용자에게 보여주고, 사용자가 계속 진행할지 묻습니다. (**Human-in-the-loop**)\n",
    "\n",
    "사용자가 'y'가 아닌 다른 입력을 하면 반복 중단됩니다.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 계산할 질문 설정\n",
    "question = \"114.5 + 121.2 + 34.2 + 110.1 의 계산 결과는?\"\n",
    "\n",
    "# agent_executor를 반복적으로 실행\n",
    "for step in agent_executor.iter({\"input\": question}):\n",
    "    if output := step.get(\"intermediate_step\"):\n",
    "        action, value = output[0]\n",
    "        if action.tool == \"add_function\":\n",
    "            # Tool 실행 결과 출력\n",
    "            print(f\"\\nTool Name: {action.tool}, 실행 결과: {value}\")\n",
    "        # 사용자에게 계속 진행할지 묻습니다.\n",
    "        _continue = input(\"계속 진행하시겠습니다? (y/n)?:\\n\") or \"Y\"\n",
    "        # 사용자가 'y'가 아닌 다른 입력을 하면 반복 중단\n",
    "        if _continue.lower() != \"y\":\n",
    "            break\n",
    "\n",
    "# 최종 결과 출력\n",
    "if \"output\" in step:\n",
    "    print(step[\"output\"])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "langchain-kr-lwwSZlnu-py3.11",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
